package org.budo.jdbc.driver;

import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.budo.support.java.net.util.JavaNetUtil;
import org.budo.support.lang.util.NumberUtil;
import org.budo.support.servlet.util.QueryStringUtil;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;

/**
 * @author lmw
 */
@Slf4j
@Getter
@Setter
@ToString
@NoArgsConstructor
@Accessors(chain = true)
public class JdbcUrl implements Serializable {
    private static final long serialVersionUID = -1865462863764167154L;

    private String url;

    private String host;

    private Integer port;

    private String catalog;

    private Map<String, String[]> parameters;

    // Group 2
    private String username;

    private String password;

    private String driver;

    public JdbcUrl(Integer port, String username, String password) {
        this.setPort(port);
        this.setUsername(username);
        this.setPassword(password);
    }

    public JdbcUrl(String url, String driver) {
        this.setUrl(url);
        this.setDriver(driver);
    }

    public JdbcUrl(String url, String host, Integer port, String catalog, Map<String, String[]> parameters) {
        this.setUrl(url);
        this.setHost(host);
        this.setPort(port);
        this.setCatalog(catalog);
        this.setParameters(parameters);
    }

    public static boolean acceptsURL(String url, String[] urlPrefix) {
        if (null == url || url.isEmpty() || null == urlPrefix || urlPrefix.length < 1) {
            log.error("#43 JdbcUrl#parse return null, url=" + url + ", urlPrefix=" + Arrays.toString(urlPrefix));
            return false;
        }

        String urlLowerCase = url.toLowerCase();
        for (String e : urlPrefix) {
            if (urlLowerCase.startsWith(e)) {
                return true;
            }
        }

        return false;
    }

    public static JdbcUrl parse(String url, String[] urlPrefix, Integer defaultPort) {
        if (!acceptsURL(url, urlPrefix)) {
            throw new IllegalArgumentException("#43 JdbcUrl#parse return null, url=" + url + ", urlPrefix=" + Arrays.toString(urlPrefix) + ", defaultPort=" + defaultPort);
        }

        int hostStart = 0;
        for (String e : urlPrefix) {
            if (url.startsWith(e)) {
                hostStart = e.length();
            }
        }

        int hostEnd = url.indexOf(':', hostStart);
        if (hostEnd < 0) { // 未指定端口
            hostEnd = url.indexOf('/', hostStart);
        }

        if (hostEnd < 0) { // 未指定库名
            hostEnd = url.length();
        }

        String host = url.substring(hostStart, hostEnd);
        Integer port;

        int portEnd = url.indexOf('/', hostEnd + 1);
        if (portEnd > -1 && portEnd > hostEnd + 1) {
            String _port = url.substring(hostEnd + 1, portEnd);
            port = Integer.parseInt(_port);
        } else if (null == defaultPort) {
            throw new RuntimeException("#92 no port or default port, url=" + url + ", defaultPort=" + defaultPort);
        } else {
            port = defaultPort;
        }

        int indexStart = Math.max(portEnd, hostEnd);
        int parameterStart = url.indexOf('?');

        Map<String, String[]> parameters;
        String catalog;

        if (parameterStart < 0) { // 未指定参数
            catalog = url.substring(indexStart + 1);
            parameters = new HashMap<String, String[]>();
        } else {
            catalog = url.substring(indexStart + 1, parameterStart);
            parameters = QueryStringUtil.queryStringToMap(url.substring(parameterStart));
        }

        // 检查网络可达
        if (null != port && port > 0 && !JavaNetUtil.isPortUsing(host, port)) {
            throw new RuntimeException("#103 can not connect url=" + url + ", host=" + host + ", port=" + port);
        }

        return new JdbcUrl(url, host, port, catalog, parameters);
    }

    public Integer getCatalogAsInteger() {
        return NumberUtil.toInteger(this.getCatalog());
    }

    public static JdbcUrl parse(String url, String[] urlPrefix) {
        return parse(url, urlPrefix, null);
    }

    public static String findPrefix(String url) {
        if (null == url || url.isEmpty()) {
            return url;
        }

        int a = url.indexOf("://");
        if (a < 0) {
            return url;
        }

        return url.substring(0, a + 3);
    }
}